#include<stdio.h> #include<string.h> #define il inline #define int long long usingnamespace std; constint P=2520;
int tt,d[19],L,mp[P+1],f[20][P][49];
il intgcd(int a,int b){return b?gcd(b,a%b):a;}
il intlcm(int a,int b){return a*b/gcd(a,b);}
il intdp(int p,int s,int c,int o) { if (!p) return s%c==0; if (!o&&~f[p][s][mp[c]]) return f[p][s][mp[c]]; int i,k=o?d[p]:9,res=0; for (i=0; i<=k; i++) res+=dp(p-1,(s*10+i)%P,lcm(c,i?i:1),o&&i==k); if (!o) f[p][s][mp[c]]=res; return res; }
il intcal(int x) { for (L=0; x; x/=10) d[++L]=x%10; returndp(L,0,1,1); }
signedmain() { freopen("num.in","r",stdin),freopen("num.out","w",stdout); scanf("%lld",&tt),memset(f,-1,sizeof f); int l,r; for (l=1,r=0; l<=P; l++) if (P%l==0) mp[l]=++r; while (tt--) scanf("%lld%lld",&l,&r),printf("%lld\n",cal(r)-cal(l-1)); return0; }
Day2
T1 river
简要题意:给定一个长度为 m 的序列 {ai},现在是第 0 天,你要从 0 走到 n。如果现在是第 i 天,你在位置 j,你可以花费 aimodm 天走到位置 j+1,或者等待一天。n≤109,m≤106,0≤ai<m。
我们设 gi 表示目前第 i 天时到达下一个位置需要花费的最小天数。这个东西非常好求,我们只需要将数组翻倍,然后就有:
gi=max{ai,gi+1+1}
求出了 gi 我们就可以 DP 了。肯定越早到越好,因为早到可以有更多选择。我们设 fi 表示移动到位置 i 需要的最小时间,那么显然有方程:
scanf("%d%d",&m,&n); int i,x=0; for (i=0; i<n; i++) scanf("%d",a+i),a[i+n]=a[i]; for (i=n+n-1; ~i; i--) a[i]=std::min(a[i],a[i+1]+1);
for (i=1; i<=m; i++,ans+=a[x],x=(x+a[x])%n) { if (b[x]) returnprintf("%lld",f[c[b[x]+(m-b[x]+1)%(i-b[x])]]+(m-b[x]+1)/(i-b[x])*(ans-f[x])),0; f[x]=ans,c[b[x]=i]=x; }
return0; }
T2 tree
简要题意:有一颗大小为 N 的树,树上每条边有一个数字,有 M 次询问,每次询问一对 (x,y),你需要回答从 x 到 y 的路径的数字任意排列是否能构成一个回文的序列。每次询问的 (x,y) 是通过某种方式生成的,最后输出回答 Yes 的个数和。N≤5×106,M≤2×107。
#include<stdio.h> #include<unordered_map> #define il inline #define int long long usingnamespace std; constint N=5e6+5; unsignedint rn=233;
int n,m,ans,val[N]; int to[N+N],nx[N+N],wt[N+N],hd[N],sze; unordered_map<int,int> o1,o2;
il intRand(){rn=rn>>5^rn,rn=rn<<17^rn,rn=rn>>9^rn,rn>>=1;return rn;}
il chargc() { staticchar buf[1<<20],*ss,*tt; if (tt==ss) { tt=(ss=buf)+fread(buf,1,1<<20,stdin); if (tt==ss) return EOF; } return *ss++; }
il intread() { int res; char c; while ((c=gc())<'0'||c>'9'); res=c^48; while ((c=gc())>='0'&&c<='9') res=(res<<3)+(res<<1)+(c^48); return res; }
il voidadd(int u,int v,int w){to[++sze]=v,nx[sze]=hd[u],wt[sze]=w,hd[u]=sze;}
il intget(int v) { if (!o1[v]) o2[o1[v]=Rand()]=v; return o1[v]; }
il voiddfs(int u,int F) { int i,v; for (i=hd[u]; i; i=nx[i]) if ((v=to[i])!=F) val[v]=val[u]^get(wt[i]),dfs(v,u); }
il intcheck(int v){return !v||o2.count(v);}
signedmain() { freopen("tree.in","r",stdin),freopen("tree.out","w",stdout); n=read(),m=read(); int i,u,v,w; for (i=1; i<n; i++) u=read(),v=read(),w=read(),add(u,v,w),add(v,u,w); dfs(1,0); for (u=read(),v=read(); m; m--) { if (check(val[u%n+1]^val[v%n+1])) ans++; u=666073ll*u%1000000007ll,v=233ll*v%998244353ll; } printf("%lld",ans); return0; }
#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> #define il inline usingnamespace std;
int n,r,p,s; string ans;
il string dfs(int c,int x) { if (!c) return string{x}; string a=dfs(c-1,x),b=dfs(c-1,x%3+1); if (a<b) return a+b; return b+a; }
il intcheck() { int o[4]={0,p,r,s}; for (auto c:ans) if (--o[c]<0) return0; return1; }
intmain() { freopen("rps.in","r",stdin),freopen("rps.out","w",stdout); scanf("%d%d%d",&r,&p,&s); int i; for (i=1; i<21; i++) if (r+p+s==1<<i) n=i; for (i=1; i<4; i++) if (ans=dfs(n,i),check()) { for (auto c:ans) { if (c==1) putchar('P'); if (c==2) putchar('R'); if (c==3) putchar('S'); } return0; } puts("IMPOSSIBLE"); return0; }
T2 vote
简要题意:有 n 个人参加了投票游戏。每个人会投支持票或者反对票,第 i 个人投支持票的概率是 pi。选出 k 个人使平票的概率最大。2≤n≤2000,2≤k≤n,2∣k,∀1≤i≤n,0≤pi≤1。
#include<stdio.h> #define il inline usingnamespace std;
int tt,n; char s[3]; int tuc[50],tuf[50];
il intid() { if (s[1]=='m') return0+s[0]-'0'; if (s[1]=='p') return11+s[0]-'0'; if (s[1]=='s') return22+s[0]-'0'; }
il intcheck() { int i,j,o; for (i=1; i<32; i++) if (tuc[i]>1) { for (j=1,tuc[i]-=2,o=1; j<34; j++) tuf[j]=tuc[j]; for (j=1; j<34; j++) { if (tuf[j]<0){o=0;break;} tuf[j]%=3,tuf[j+1]-=tuf[j],tuf[j+2]-=tuf[j]; } tuc[i]+=2; if (o) return1; } return0; }
intmain() { freopen("mj.in","r",stdin),freopen("mj.out","w",stdout); scanf("%d%d",&n,&tt); int i; while (tt--) { for (i=1; i<34; i++) tuc[i]=0; for (i=1; i<=n; i++) scanf("%s",s),tuc[id()]++; if (n==14) printf("%d\n",check()); else { for (i=1; i<10; i++) if (tuc[i]<4) { tuc[i]++; if (check()) printf("%dm ",i); tuc[i]--; } for (i=12; i<21; i++) if (tuc[i]<4) { tuc[i]++; if (check()) printf("%dp ",i-11); tuc[i]--; } for (i=23; i<32; i++) if (tuc[i]<4) { tuc[i]++; if (check()) printf("%ds ",i-22); tuc[i]--; } puts(""); } } return0; }
T2 tree
简要题意:给你一棵有 N 个节点的有根树,根节点编号为 1,树上每一个节点有一个权值,你需要维护这棵树进行描述如下的 M 次操作:
1 u v: 将点 u 的权值加上 v。
2 a b v:将覆盖 a,b 的点数最少的子树的所有点的权值加上 v。
3 a b: 查询覆盖 a,b 的点数最少的子树的根节点编号 id 及权值 valid。
4 u:查询以节点 u 为根的子树的节点权值之和 sumu。
由于权值可能很大,所有的查询结果都请对 109+7 取模。
#include<stdio.h> #include<algorithm> #define il inline usingnamespace std; constint N=5e5+1,P=1e9+7;
int n,m,tot,fa[N],val[N],dep[N]; int to[N],nx[N],head[N],sze; int sz[N],son[N],top[N],dfn[N],rev[N]; int t[4*N],lz[4*N];
il voidbuild(int p,int l,int r) { if (l==r){t[p]=val[rev[l]]%P;return;} int mid=l+r>>1; build(p<<1,l,mid),build(p<<1|1,mid+1,r),t[p]=(t[p<<1]+t[p<<1|1])%P; }
il voidtg(int p,int l,int r,int v){lz[p]=(lz[p]+v)%P,t[p]=(t[p]+1ll*(r-l+1)*v%P)%P;}
il voidpushdown(int p,int l,int r,int mid){if (lz[p]) tg(p<<1,l,mid,lz[p]),tg(p<<1|1,mid+1,r,lz[p]),lz[p]=0;}
il voiduptqj(int p,int l,int r,int x,int y,int v) { if (l>=x&&r<=y) returntg(p,l,r,v); int mid=l+r>>1; pushdown(p,l,r,mid); if (x<=mid) uptqj(p<<1,l,mid,x,y,v); if (y>mid) uptqj(p<<1|1,mid+1,r,x,y,v); t[p]=(t[p<<1]+t[p<<1|1])%P; }
il intquery(int p,int l,int r,int x,int y) { if (l>=x&&r<=y) return t[p]; int mid=l+r>>1,res=0; pushdown(p,l,r,mid); if (x<=mid) res=(res+query(p<<1,l,mid,x,y))%P; if (y>mid) res=(res+query(p<<1|1,mid+1,r,x,y))%P; return res; }
il voidadd(int u,int v){to[++sze]=v,nx[sze]=head[u],head[u]=sze;}
il intLCA(int x,int y) { for ( ; top[x]!=top[y]; x=fa[top[x]]) if (dep[top[x]]<dep[top[y]]) swap(x,y); return dep[x]<dep[y]?x:y; }
il voiddfs1(int u,int F) { dep[u]=dep[F]+1,sz[u]=1; int i,v; for (i=head[u]; i; i=nx[i]) { v=to[i],dfs1(v,u),sz[u]+=sz[v]; if (sz[v]>sz[son[u]]) son[u]=v; } }
il voiddfs2(int u,int t) { top[u]=t,dfn[u]=++tot,rev[tot]=u; int i,v; if (son[u]) dfs2(son[u],t); for (i=head[u]; i; i=nx[i]) if (!top[v=to[i]]) dfs2(v,v); }
intmain() { freopen("tree.in","r",stdin),freopen("tree.out","w",stdout); scanf("%d%d",&n,&m); int i,x,y,v,l; for (i=2; i<=n; i++) scanf("%d",fa+i),add(fa[i],i); for (i=1; i<=n; i++) scanf("%d",val+i); dfs1(1,0),dfs2(1,1),build(1,1,n); while (m--) { scanf("%d%d",&i,&x); if (i==1) scanf("%d",&v),uptqj(1,1,n,dfn[x],dfn[x],v); if (i==2) scanf("%d%d",&y,&v),l=LCA(x,y),uptqj(1,1,n,dfn[l],dfn[l]+sz[l]-1,v); if (i==3) scanf("%d",&y),l=LCA(x,y),printf("%d %d\n",l,query(1,1,n,dfn[l],dfn[l])); if (i==4) printf("%d\n",query(1,1,n,dfn[x],dfn[x]+sz[x]-1)); } return0; }
T3 permutation
简要题意:对于这个排列 P,给一个定义:一个区间 [L,R] 被称为「美丽的」,当且仅当 P 的这个区间是元素连续的区间。也就是说,如果区间 [L,R] 满足该区间内元素升序排序之后是一个公差为 1 的等差数列。现在有 M 个区间 [li,ri],求包含这个区间的最小的「美丽的」区间 [Li,Ri]。N,M≤105。
int n,m,a[N],b[N],L[N],R[N]; int f[2][N][17],lg[N]; int t,s[N],ok[N],bel[N]; vector<int> g1[N],g2[N];
il voiddfs1(int u) { ok[u]=1; for (auto v:g1[u]) if (!ok[v]) dfs1(v); s[++t]=u; }
il voiddfs2(int u,int c) { bel[u]=c,L[c]=min(L[c],u),R[c]=max(R[c],u); for (auto v:g2[u]) if (!bel[v]) dfs2(v,c); }
il voidinit(int t,int *y) { int i,j; for (i=1; i<=n; i++) f[t][i][0]=y[i]; for (j=1; j<17; j++) for (i=1; i+(1<<j)-1<=n; i++) { if (t&1) f[t][i][j]=max(f[t][i][j-1],f[t][i+(1<<j-1)][j-1]); else f[t][i][j]=min(f[t][i][j-1],f[t][i+(1<<j-1)][j-1]); } }
il intquery(int t,int l,int r) { int s=lg[r-l+1]; if (t&1) returnmax(f[t][l][s],f[t][r-(1<<s)+1][s]); returnmin(f[t][l][s],f[t][r-(1<<s)+1][s]); }
scanf("%d",&n); int i,x,y; for (i=1,lg[0]=-1; i<=n; i++) scanf("%d",a+i),b[a[i]]=i,lg[i]=lg[i>>1]+1; init(0,b),init(1,b); for (i=1; i<n; i++) x=min(a[i],a[i+1]),y=max(a[i],a[i+1]), L[i]=query(0,x,y),R[i]=query(1,x,y)-1; for (i=1; i<n; i++) { while (t&&R[s[t]]<i) t--; if (t) g1[s[t]].pb(i),g2[i].pb(s[t]); s[++t]=i; } for (i=n-2; i; i--) { while (t&&L[s[t]]>i) t--; if (t) g1[s[t]].pb(i),g2[i].pb(s[t]); s[++t]=i; } for (i=1; i<n; i++) L[i]=1e9,R[i]=0; for (i=1,t=0; i<n; i++) if (!ok[i]) dfs1(i); for (i=t; i; i--) if (!bel[s[i]]) dfs2(s[i],s[i]); for (i=1; i<n; i++) for (auto j:g1[s[i]]) if (bel[s[i]]!=bel[j]) L[bel[s[i]]]=min(L[bel[s[i]]],L[bel[j]]),R[bel[s[i]]]=max(R[bel[s[i]]],R[bel[j]]); for (i=1; i<n; i++) L[i]=L[bel[i]],R[i]=R[bel[i]]; init(0,L),init(1,R); for (scanf("%d",&m); m; m--) { scanf("%d%d",&x,&y); if (x==y) printf("%d %d\n",x,y); else y--,printf("%d %d\n",query(0,x,y),query(1,x,y)+1); } return0; }
intmain() { freopen("cube.in","r",stdin),freopen("cube.out","w",stdout); int k,l; For(i,j) scanf("%d",a[i]+j); for (k=1; k<4; k++) { foR(i) scanf("%d",b[k]+i); foR(i) scanf("%d",c[k]+i); foR(i) scanf("%d",d[k]+i); } For(i,j) scanf("%d",e[i]+j); For(i,j) scanf("%d",f[i]+j); scanf("%s",q),l=strlen(q); for (int i=0; i<l; i++) { if (q[i]=='a') _a(); if (q[i]=='A') _A(); if (q[i]=='b') _b(); if (q[i]=='B') _B(); if (q[i]=='c') _c(); if (q[i]=='C') _C(); if (q[i]=='d') _d(); if (q[i]=='D') _D(); if (q[i]=='e') _e(); if (q[i]=='E') _E(); if (q[i]=='f') _f(); if (q[i]=='F') _F(); if (q[i]=='h') _h(); if (q[i]=='H') _H(); if (q[i]=='i') _i(); if (q[i]=='I') _I(); if (q[i]=='j') _j(); if (q[i]=='J') _J(); } For(i,j) printf("%d%c",a[i][j]," \n"[j==3]); for (k=1; k<4; k++) { foR(i) printf("%d ",b[k][i]); foR(i) printf("%d ",c[k][i]); foR(i) printf("%d ",d[k][i]); puts(""); } For(i,j) printf("%d%c",e[i][j]," \n"[j==3]); For(i,j) printf("%d%c",f[i][j]," \n"[j==3]);
return0; }
T2 tree
简要题意:有一颗 n 个点的树,又有 m 条加边,第 i 条加边连接 ui,vi 两个点。求有多少种方案,要求割掉一条原树中的边和一条加边,使得原图不连通。1≤n,m≤3×105。
本题为四校原题~~(说明省夏今天的出题人懒)~~,以下树边均指原树中的边。
显然加边 (x,y) 相当于给树边简单路径 (x,y) 加上了保险,覆盖了一遍这条路径。
考虑如果切断一条树边,要怎样选择加边,使得原图不联通。分三类情况讨论:
这条边只被一个路径覆盖,则切断了这条边,只有把对应路径的加边切断才可以。
这条边没有被任何一个路径覆盖,那么只要切断了这条边,原图就已经不联通了,剩下的 m 条加边切断那一条都可以。
#include<stdio.h> #include<algorithm> #define il inline usingnamespace std; constint N=3e5+5;
int n,m,dep[N],fa[N][21],val[N]; longlong ans; int to[N+N],nx[N+N],hd[N],sze;
il voidadd(int u,int v){to[++sze]=v,nx[sze]=hd[u],hd[u]=sze;}
il intLCA(int x,int y) { if (dep[x]<dep[y]) swap(x,y); int i; for (i=20; ~i; i--) if (dep[fa[x][i]]>=dep[y]) x=fa[x][i]; if (x==y) return x; for (i=20; ~i; i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; }
il voidinit(int u,int F) { dep[u]=dep[F]+1; int i,v; for (i=0; i<20; i++) fa[u][i+1]=fa[fa[u][i]][i]; for (i=hd[u]; i; i=nx[i]) if ((v=to[i])!=F) init(v,fa[v][0]=u); }
il voiddfs(int u,int F){for (int i=hd[u],v; i; i=nx[i]) if ((v=to[i])!=F) dfs(v,u),val[u]+=val[v];}
intmain() { freopen("tree.in","r",stdin),freopen("tree.out","w",stdout); scanf("%d%d",&n,&m); int i,u,v; for (i=1; i<n; i++) scanf("%d%d",&u,&v),add(u,v),add(v,u); init(1,0); for (i=1; i<=m; i++) scanf("%d%d",&u,&v),val[u]++,val[v]++,val[LCA(u,v)]-=2; for (dfs(1,0),i=2; i<=n; i++) { if (val[i]==0) ans+=1ll*m; if (val[i]==1) ans++; } printf("%lld",ans); return0; }
T3 missile
简要题意:三维导弹拦截。1≤n≤1000。所有数字均不超过 106。
第一个问题直接 O(n2) DP 即可。
第二个问题:若打完 a 可以打 b,则 a 向 b 连一条有向边,那么答案就是这个 DAG 的最小路径覆盖,直接 dinic 或者别的算法解决,复杂度为 O(NM),其中 N,M 分别为网络流图的点数与边数。
#include<stdio.h> #include<string.h> #include<algorithm> #define il inline usingnamespace std; constint N=2005,M=N*N;
int n,ans,S,T,f[N],lev[N],it[N],q[M],h,t; int to[M],nx[M],cap[M],head[N],sze=1; structnode{int x,y,z;}p[N];
il boolcmp(node a,node b){return a.x<b.x;}
il voidadd(int u,int v,int c){to[++sze]=v,nx[sze]=head[u],cap[sze]=c,head[u]=sze;}
il voidins(int u,int v,int c){add(u,v,c),add(v,u,0);}
il intbfs() { memset(lev,0,4*T+4),q[h=t=lev[S]=1]=S; int i,u,v; while (h<=t) for (i=head[u=q[h++]]; i; i=nx[i]) if (!lev[v=to[i]]&&cap[i]) lev[v]=lev[u]+1,q[++t]=v; return lev[T]; }
il intdfs(int u,int f) { if (u==T) return f; int v,z,res=0; for (int &i=it[u]; i; i=nx[i]) if (cap[i]&&lev[v=to[i]]==lev[u]+1&&(z=dfs(v,min(cap[i],f-res)))) { cap[i]-=z,cap[i^1]+=z,res+=z; if (res==f) break; } return res; }
il intdinic() { int f,res=0; while (bfs()) { memcpy(it,head,4*T+4); while (f=dfs(S,1e9)) res+=f; } return res; }
intmain() { freopen("missile.in","r",stdin),freopen("missile.out","w",stdout); scanf("%d",&n),S=n+n+1,T=S+1; int i,j; for (i=1; i<=n; i++) scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z); sort(p+1,p+n+1,cmp);
for (i=1; i<=n; i++) { f[i]=1,ins(S,i,1),ins(i+n,T,1); for (j=1; j<i; j++) if (p[j].x<p[i].x&&p[j].y<p[i].y&&p[j].z<p[i].z) f[i]=max(f[i],f[j]+1),ins(j,i+n,1); ans=max(ans,f[i]); } printf("%d\n%d",ans,n-dinic()); return0; }
Day7
T1 abc
简要题意:共有 n 组询问,每次给定一个三个顶点都在格点上的三角形,求这个三角形的边界加内部共有多少个格点。
#include<stdio.h> #include<math.h> #include<algorithm> #define il inline #define int long long usingnamespace std;
int tt,ax,ay,bx,by,cx,cy,ans;
il intgcd(int a,int b){return b?gcd(b,a%b):a;}
il intcal(int x,int y) { x=abs(x),y=abs(y); if (!x||!y) return0; return ((x+1ll)*(y+1ll)-gcd(x,y)-1ll)/2ll; }
il intcheck(int x,int y,int z){return (y-x)*(z-x)<0;}
signedmain() { freopen("abc.in","r",stdin),freopen("abc.out","w",stdout); scanf("%lld",&tt); int X1,Y1,X2,Y2; while (tt--) { scanf("%lld%lld%lld%lld%lld%lld",&ax,&ay,&bx,&by,&cx,&cy); ans=(max(ax,max(bx,cx))-min(ax,min(bx,cx))+1ll)*(max(ay,max(by,cy))-min(ay,min(by,cy))+1ll)-cal(ax-bx,ay-by)-cal(ax-cx,ay-cy)-cal(bx-cx,by-cy); if (check(ax,bx,cx)&&check(ay,by,cy)) ans-=min(abs((cx-ax)*(ay-by)),abs((ax-bx)*(cy-ay))); if (check(bx,ax,cx)&&check(by,ay,cy)) ans-=min(abs((cx-bx)*(by-ay)),abs((bx-ax)*(cy-by))); if (check(cx,bx,ax)&&check(cy,by,ay)) ans-=min(abs((cx-ax)*(cy-by)),abs((cx-bx)*(cy-ay))); printf("%lld\n",ans); }
return0; }
T2 file
简要题意:求一条从圆上出发,经过折射 / 反射后最终沿 x 轴正方向射到原点的光路。
考虑到光路可逆,问题转化为求一条从原点出发沿 x 轴正方向射到圆上的光路。
代码没写。
T3 see
简要题意:有 n 条形如 y=kx+m 的直线,求可以从 y 正负无穷远处看到的(只看到一个点的不算)直线的编号和。n≤105,∣k∣,∣m∣≤109。
#include<stdio.h> #include<algorithm> #define il inline #define int long long usingnamespace std; constint N=1e5+5;
int n,ans,t; structvec{int x,y,id;}v[N],s[N]; il vec operator - (vec a,vec b){return (vec){a.x-b.x,a.y-b.y};} il intoperator & (vec a,vec b){return a.x*b.y-a.y*b.x;} il booloperator < (vec a,vec b){return a.x==b.x?a.y<b.y:a.x<b.x;}
il voidconvex() { sort(v+1,v+n+1); int i; for (i=1; i<=n; s[++t]=v[i++]) for ( ; t>1&&((s[t]-s[t-1])&(v[i]-s[t]))<=0; t--); for (i=1; i<=t; i++) ans+=s[i].id; for (i=1,t=0; i<=n; s[++t]=v[i++]) for ( ; t>1&&((s[t]-s[t-1])&(v[i]-s[t]))>=0; t--); for (i=2; i<t; i++) ans+=s[i].id; }
signedmain() { freopen("see.in","r",stdin),freopen("see.out","w",stdout); scanf("%lld",&n); int i; for (i=1; i<=n; i++) scanf("%lld%lld",&v[i].x,&v[i].y),v[i].id=i; convex(),printf("%lld",ans); return0; }
Day8
T1 split
简要题意:有 n 堆石子,每堆石子有 xi 个。两个人玩组合游戏,每次行动可以在以下两种操作中选择一种进行,无法进行操作的玩家就失败了。
选择一堆非空石子,拿走一个。
选择一堆石子数为偶数的非空石子堆,假设石子数为 2x,那么拿走这堆石子,然后增加 K 堆石子数为 x 的石子堆。
问先还是后手获胜,T 组数据。
1≤T≤10,1≤n≤50000,1≤K,xi≤109。
若 K 是偶数,那么转移到的状态一定是 0。而 SG1=1,SG2=2,其他奇数的都是 0,偶数的都是 1。
若 K 是奇数,可以发现除了前几位以外,奇数位置的 SG 值都是 0。我们可以暴力 O(log) 计算。
于是暴力求 SG 函数算出 Nim 积即可,时间复杂度为 O(Tnlog),空间复杂度为 O(1)。
il intSG(int x) { int a,b; if (m&1^1) return x<2?1:(x<3?2:x&1^1); else { if (x==1||x==3) return1; if (x&1) return0; return a=SG(x-1),b=SG(x>>1),a&&b?0:(a==1||b==1?2:1); } return0; }
#include<stdio.h> #include<string.h> #include<unordered_map> #define il inline usingnamespace std; constint N=5e5+5,M=1.5e6,F=0X3f3f3f;
int n,m,cnt,dis[N],q[M],h,t; int to[M],nx[M],wt[M],hd[N],sze; unordered_map<int,int> o[N];
il intid(int x,int c){return o[x][c]?o[x][c]:o[x][c]=++cnt;}
il voidadd(int u,int v,int w){to[++sze]=v,nx[sze]=hd[u],wt[sze]=w,hd[u]=sze;}
il voidbfs() { memset(dis,F,4*cnt+4),dis[q[h=t=1]=1]=0; int i,u,v; while (h<=t) for (i=hd[u=q[h++]]; i; i=nx[i]) if (dis[v=to[i]]>dis[u]+wt[i]) dis[v]=dis[u]+wt[i],q[++t]=v; }
intmain() { freopen("transfer.in","r",stdin),freopen("transfer.out","w",stdout); scanf("%d%d",&n,&m),cnt=n; int u,v,c,x,y; while (m--) scanf("%d%d%d",&u,&v,&c),x=id(u,c),y=id(v,c), add(u,x,1),add(x,u,1),add(v,y,1),add(y,v,1),add(x,y,0),add(y,x,0); bfs(),printf("%d",dis[n]==F?-1:dis[n]/2); return0; }
T3 number
简要题意:有 n 个数 p1∼n,求有多少种排列方式,使得这些数字顺次拼接起来得到的数字的是 11 的倍数,答案对 998,244,353 取模,多组数据。1≤∑n,T≤2000,1≤ai≤109。
注意到 11≡−1(mod11),所以 10k≡±1。而在一个数 x 前面加上另一个数 y 得到的数字是 y×10k+x≡±1×y+x(mod11),而是乘上 1 还是 −1 则属由 x 的位数来决定的。综合前面两个结论,我们可以得出,每个数对最后模 11 意义下的贡献是它本身或者它的相反数。我们设对应 1 的序列为 a1∼A,对应 −1 的序列为 b1∼B。
#include<bits/stdc++.h> #define il inline #define ll long long constint N=2005,P=998244353;
int tt,n,A,B,ans,a[N],b[N],fac[N],f[N][N][11],g[N][N][11];
intmain() { freopen("number.in","r",stdin),freopen("number.out","w",stdout); scanf("%d",&tt); int i,j,k,x; for (i=fac[0]=1; i<N; i++) fac[i]=(ll)fac[i-1]*i%P; while (tt--) { scanf("%d",&n),ans=A=B=0; for (i=1; i<=n; i++) { scanf("%d",&x); for (k=x,j=1; k; k/=10) j++; (j&1?a[++A]:b[++B])=x%11; } for (i=0; i<=A; i++) for (j=0; j<=A; j++) for (k=0; k<11; k++) f[i][j][k]=0; for (i=0; i<=B; i++) for (j=0; j<=B; j++) for (k=0; k<11; k++) g[i][j][k]=0; g[0][0][0]=(ll)fac[B/2]*fac[B+1>>1]%P; for (i=1; i<=B; i++) for (j=0; j<=i&j+j<=B; j++) for (k=0; k<11; k++) { if (i-j<=B+1>>1) g[i][j][(k-b[i]+11)%11]=(g[i][j][(k-b[i]+11)%11]+g[i-1][j][k])%P; if (j) g[i][j][(k+b[i])%11]=(g[i][j][(k+b[i])%11]+g[i-1][j-1][k])%P; } for (k=0; k<11; k++) f[0][0][k]=g[B][B/2][k]; for (i=1; i<=A; i++) for (j=0; j<i; j++) for (k=0; k<11; k++) f[i][j+1][(k+a[i])%11]=(f[i][j+1][(k+a[i])%11]+(ll)f[i-1][j][k]*(B/2+j+1))%P,f[i][j][(k-a[i]+11)%11]=(f[i][j][(k-a[i]+11)%11]+(ll)f[i-1][j][k]*((B+1)/2-1+i-j))%P; for (j=0; j<=A; j++) ans=(ans+f[A][j][0])%P; printf("%d\n",ans); } return0; }